#include "listener.h"
#include <sys/socket.h>
#include <cstring>

#ifndef _CANARY_EXCEPT_H_
#include "../util/canary_except.h"
#endif

#include "../coro/epoll_suspended_manager.h"

namespace wilson::net
{
    listener::listener(socket& sock)
        : l_sock{ sock }
    {
        ::listen(sock.fd(), 5000);
    }

    ::std::pair<bool, tcp_conn> listener::
    accept_nonblk()
    {
        int ret = ::accept(
            l_sock.fd(), 
            nullptr, nullptr
        );

        if (ret == -1)
        {
            // no connection returned by this call (nonblk)
            if (errno == EINTR || errno == EAGAIN)
            {
                return { false, tcp_conn{} };
            }
            // error
            else
            {
                throw ::std::runtime_error{ ::strerror(errno) };
            }
        }

        sys::fd_guard connfd{ ret };
        connfd.set_nonblk();
        tcp_conn new_conn{ ::std::move(connfd) };
        
        return { true, ::std::move(new_conn) };
    }

    bool listener::accept_awtb::
    await_ready() noexcept
    try {
        auto [ok, conn] = aa_listener.accept_nonblk();
        if (ok)
        {
            aa_result = ::std::move(conn);
            return true;
        }
        else
        {
            return false;
        }
    } catch (...) {
        aa_errno = errno;
        return true;
    }

    void listener::accept_awtb::
    await_suspend(::std::coroutine_handle<> h) 
    {
        aa_esm.add_task(aa_listener.l_sock.fd(), EPOLLIN, h);
    }
    
    tcp_conn listener::accept_awtb::
    await_resume()
    {
        if (aa_result.empty())
        {
            auto [ok, conn] = aa_listener.accept_nonblk();
            if (ok) [[likely]] return conn;
        }
        else if (aa_errno) [[unlikely]]
        {
            throw ::std::runtime_error{ ::strerror(aa_errno) };
        }

        return ::std::move(aa_result);
    }

    [[nodiscard]]
    listener::accept_awtb 
    listener::coro_accept(coro::epoll_suspend_manager& esm)
    {
        return { esm, *this };
    }
}
